module Optimiz where

import Data.List
import Data.List.Split

repl_pair pos (val, val') list = 
  take pos list ++ val : val' : drop (pos+2) list
drop_pair pos list = 
  take pos list ++ drop (pos+2) list
pair x = (head lst, last lst)
  where
    lst = splitOn " " x

commut_opt :: String -> [String] -> [String] -> Int -> [String]
commut_opt _ (x:[]) code _ = code
commut_opt oper (x:x':xs) code num = 
  if op == "LOAD" && op' == oper && x' `notElem` xs 
  then commut_opt oper (x':xs) n_code (num+1)
  else commut_opt oper (x':xs) code (num+1)
  where
    (op, name) = pair x
    (op', name') = pair x'
    new = (op ++ ' ': name', oper ++ ' ': name)
    n_code = repl_pair num new code

sl_opt :: [String] -> [String] -> Int -> [String]
sl_opt (x:[]) code _ = code
sl_opt (x:x':xs) code num = 
  if op == "STORE" && op' == "LOAD" && name == name'
    && (and $ map (notElem name) (map (splitOn " ") xs))
  then sl_opt (xs) n_code num
  else sl_opt (x':xs) code (num+1)
  where
    (op, name) = pair x
    (op', name') = pair x'
    n_code = drop_pair num code

ls_opt :: [String] -> [String] -> Int -> [String]
ls_opt (_:_:[]) code _ = code
ls_opt (x:x':x'':xs) code num = 
  if op == "LOAD" && op' == "STORE" && op'' == "LOAD"
  then ls_opt (x'':xs) n_code num
  else ls_opt (x':x'':xs) code (num+1)
  where
    (op, name) = pair x
    (op', name') = pair x'
    (op'', _) = pair x''
    code' = take num code
    code'' = map (repl_name_if name' name) (x'':xs)
    n_code = code' ++ code'' 
    repl_name_if pattern subst x = 
      if name == pattern 
      then op ++ ' ':subst
      else x
      where
        (op, name) = pair x

optimiz :: [String] -> [String]
optimiz code = res
  where
    a = commut_opt "ADD" code code 0
    b = commut_opt "MPY" a a 0
    c = sl_opt b b 0
    res = ls_opt c c 0
